using CfgTable;
using Cysharp.Threading.Tasks;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using NetProtocol.POD;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace IQIGame.Onigao.GamePlay
{
    public class MapUIModule : Singleton<MapUIModule>
    {
        private Dictionary<int, LevelEntityConfig> _tempLevelConfigLib = new();

        private MapUIModule()
        {
        }

        public async UniTask<MapUIData> GetMapData(GameplayAreaType targetMapArea, int regionCid)
        {
            switch (targetMapArea)
            {
                case GameplayAreaType.MainCity:
                    return await GetMainCityMapData(regionCid);
                case GameplayAreaType.Level:
                    return await GetLevelMapData(regionCid);
                default:
                    return null;
            }
        }

        /// <summary>
        /// 获取主城地区数据
        /// 不管是当前主城区域还是未进入的主城区域，都通过该方法获取地图
        /// 不是从现有实体内存数据取值，而是统一从配置和服务器数据中取值
        /// </summary>
        /// <param name="regionCid"></param>
        /// <returns></returns>
        private async UniTask<MapUIData> GetMainCityMapData(int regionCid)
        {
            var curArea = GameplayAreaManager.Instance.curSceneArea;
            MainCitySceneConfig sceneGameplayConfig = null;
            if (curArea.areaType == GameplayAreaType.MainCity && regionCid == curArea.curRegionCid)
            {
                sceneGameplayConfig = MainCityModule.Instance.mainController.regionCtrl.sceneGameplayConfig;
            }
            else
            {
                var cfgRegion = TableCenter.regionInstance.Get(regionCid);
                var cfgPath = PathConstant.GetSceneGameplayCfg(cfgRegion.GameplayConfig);
                sceneGameplayConfig = await GameplayAreaModularConfigMgr.Instance.LoadModularConfig<MainCitySceneConfig>(cfgPath);
            }
            if (sceneGameplayConfig == null)
            {
                return null;
            }
            MainCityRegionData regionSaveData = null;
            GameDataCenter.PlayerData.mainCityData?.regions?.TryGetValue(regionCid, out regionSaveData);

            var mapData = MapUIData.Create();
            foreach (var entityConfig in sceneGameplayConfig.entityConfigs)
            {
                //不是地图相关实体，略过
                if (entityConfig.mapUIEntityCid == 0)
                {
                    continue;
                }
                MainCityNpcData entitySaveData = null;
                regionSaveData?.npcs?.TryGetValue(entityConfig.id, out entitySaveData);
                //如果死亡或未激活，略过
                if (entitySaveData != null && !entitySaveData.active)
                {
                    continue;
                }
                else if (entitySaveData == null && !entityConfig.isActive)
                {
                    continue;
                }
                //组织数据
                var data = MapUIData.CreateEntityData();
                data.entityId = entityConfig.id; //主城id以mcid替代，这一点参考MainCityNormalEntity.dynNormalEntity.id的注释
                data.entityMcid = entityConfig.id;
                data.mapUIEntityCid = entityConfig.mapUIEntityCid;
                data.position = GameMathUtil.LogicPos2UnityPos(entityConfig.position);
                data.rotation = Quaternion.Euler(entityConfig.initDirEuler.x, entityConfig.initDirEuler.y, entityConfig.initDirEuler.z);
                data.isObstacleActive = entityConfig.isObstacleActive;

                BaseMainCityEntityDynParam dynParam = null;
                if (entityConfig.paramMap.TryGetValue(MainCityEntityDynParamType.CheckPoint, out dynParam))
                {
                    data.isCheckPoint = true;
                    data.isCheckPointActive = (dynParam as MainCityDynParamCheckPoint).isActive;
                }
                if (entityConfig.paramMap.TryGetValue(MainCityEntityDynParamType.QuestList, out dynParam))
                {
                    var quests = (dynParam as MainCityDynParamQuest).offerQuests;
                    foreach (var questCid in quests)
                    {
                        //为了统一接口，这里entity传null（如果目标地区不是当前地区是无法获取到实体对象的）
                        //这意味着任务是否可接取的条件里，不能用mcid == 0作为参数来指代当前实体（mcid == X，X大于0，不影响）
                        if (QuestModule.Instance.IsAcceptable(questCid, null))
                        {
                            data.isOfferingQuest = true;
                            break;
                        }
                    }
                }

                //通过存档覆盖备份配置数据
                if (entitySaveData != null)
                {
                    data.position = GameMathUtil.LogicPos2UnityPos(entitySaveData.pos);
                }

                mapData.entities.Add(data);
            }
            return mapData;
        }

        /// <summary>
        /// 获取关卡地区数据
        /// 当目标地区是当前地区（在关卡内）时直接从实时关卡数据中取值，该数据必须向逻辑层获取，并不会像主城一样一直存在于系统数据中
        /// 否则（不在关卡中，或目标地区不是当前地区），则向游戏服务器请求存档数据，再通过本地配置和存档数据组装地图数据
        /// </summary>
        /// <param name="region"></param>
        /// <returns></returns>
        private async UniTask<MapUIData> GetLevelMapData(int regionCid)
        {
            var regionCtrl = LevelPlayModule.Instance.inLevelPlay ? LevelPlayModule.Instance.levelPlay.regionCtrl : null;
            //玩家不在关卡里，或目标地区不属于当前关卡，通过存档组织数据
            if (regionCtrl == null || !regionCtrl.levelPlayCtrl.cfgLevel.Regions.Contains(regionCid))
            {
                int levelCid = GameScenePlayUtil.GetLevelCidByRegionCid(regionCid);
                if (levelCid == 0)
                {
                    LogGame.LogError($"地区 {regionCid} 不属于任何关卡");
                    return null;
                }
                return await GetFromLevelSaveData(GameplayAreaType.Level, levelCid, regionCid);
            }
            //否则向逻辑层发送请求获取数据
            var result = await CLevelLogicModule.NetCore.CS_GetMapEntities(regionCid);
            var mapData = MapUIData.Create();
            foreach (var pod in result.entities)
            {
                var data = MapUIData.CreateEntityData();
                data.entityId = pod.id;
                data.entityMcid = pod.mcid;
                data.mapUIEntityCid = pod.mapUIEntityCid;
                data.position = GameMathUtil.LogicPos2UnityPos(pod.x, pod.y, pod.z);
                data.rotation = GameMathUtil.IntEuler2Quaternion(pod.dir);
                data.isOfferingQuest = pod.isOfferingQuest;
                data.isObstacleActive = pod.isObstacleActive;
                data.isCheckPoint = pod.isCheckPoint;
                data.isCheckPointActive = pod.isCheckPointActive;
                mapData.entities.Add(data);
            }

            return mapData;
        }

        /// <summary>
        /// 通过存档组织关卡地图数据
        /// </summary>
        /// <param name="gameArea"></param>
        /// <param name="region"></param>
        /// <returns></returns>
        private async UniTask<MapUIData> GetFromLevelSaveData(GameplayAreaType gameArea, int levelCid, int regionCid)
        {
            var result = await LevelModule.NetCore.CS_getLevelSaveData(levelCid);
            if (result.IsFail)
            {
                return null;
            }
            CfgRegionInstance cfgRegion = TableCenter.regionInstance.Get(regionCid);
            var path = PathConstant.GetSceneGameplayCfg(cfgRegion.GameplayConfig);
            var gameplayConfig = await GameplayAreaModularConfigMgr.Instance.LoadModularConfig<LevelGamePlayConfig>(path);
            if (gameplayConfig == null)
            {
                return null;
            }
            //从关卡设计原理来说，是可能在运行时动态创建实体的（克隆出来的实体mcid会和已有实体重复），且存档设计上不一定包含所有实体。
            //也就意味着不论是存档还是配置，都不一定是全数据，两者重合后的集合才是。
            //所以这里要这么写：要先把所有配置数据放到库里，然后先遍历存档，最后把剩下的配置数据（无存档）组装成地图数据。
            _tempLevelConfigLib.Clear();
            foreach (var entityConfig in gameplayConfig.entityConfigs)
            {
                _tempLevelConfigLib.Add(entityConfig.id, entityConfig);
            }
            List<LevelEntityPOD> entityPods = ListPool<LevelEntityPOD>.Get();
            if (result.saveData?.saveData is LevelSavePOD levelSaveData)
            {
                if (levelSaveData.regionSaves.TryGetValue(regionCid, out var regionSavePOD))
                {
                    foreach (var pod in regionSavePOD.modifiedEntities)
                    {
                        entityPods.Add(pod);
                    }
                }
            }

            var mapData = MapUIData.Create();
            //先添加存档转换的地图数据，添加一个尝试通过mcid从库里移除一个
            foreach (var entitySavePOD in entityPods)
            {
                if (entitySavePOD.normal == null) //不是常规实体不出现在地图上（如英雄实体会特殊处理，而不是直接通过地图数据显示）
                {
                    continue;
                }
                gameplayConfig.entityMap.TryGetValue(entitySavePOD.normal.mcid, out var entityConfig);
                FillEntityDataIntoOtherMapData(mapData, entityConfig, entitySavePOD, _tempLevelConfigLib);
            }
            //添加剩下的
            foreach (var kv in _tempLevelConfigLib)
            {
                FillEntityDataIntoOtherMapData(mapData, kv.Value, null, null);
            }
            _tempLevelConfigLib.Clear();
            return mapData;
        }

        /// <summary>
        /// 往数据里填充一个实体，注意该方法仅为获取未进入关卡的地图准备
        /// </summary>
        /// <param name="mapData"></param>
        /// <param name="config"></param>
        /// <param name="savePOD"></param>
        /// <param name="configLib"></param>
        private void FillEntityDataIntoOtherMapData(MapUIData mapData, LevelEntityConfig config, LevelEntityPOD savePOD, Dictionary<int, LevelEntityConfig> configLib)
        {
            if (config.mapUIEntityCid == 0)
            {
                return;
            }
            if (savePOD != null && !savePOD.isActive)
            {
                return;
            }
            else if (savePOD == null && !config.isActive)
            {
                return;
            }
            //先通过配置组装数据
            var data = MapUIData.CreateEntityData();
            data.entityId = savePOD == null ? 0 : savePOD.id;
            data.entityMcid = config.id;
            data.mapUIEntityCid = config.mapUIEntityCid;
            data.position = GameMathUtil.LogicPos2UnityPos(config.position);
            data.rotation = Quaternion.Euler(config.initDirEuler.x, config.initDirEuler.y, config.initDirEuler.z);
            data.isObstacleActive = config.isObstacleActive;

            BaseEntityDynamicParam dynParam = null;
            if (config.paramMap.TryGetValue(EntityDynamicParamType.CheckPoint, out dynParam))
            {
                data.isCheckPoint = true;
                data.isCheckPointActive = (dynParam as EntityParamCheckPoint).isActive;
            }
            if (config.paramMap.TryGetValue(EntityDynamicParamType.QuestList, out dynParam))
            {
                var quests = (dynParam as EntityParamQuest).offerQuests;
                foreach (var questCid in quests)
                {
                    //注意如果不是当前玩家所在地图，没有实体对象，所以这里entity传null，
                    //这意味着任务是否可接取的条件里，不能用mcid == 0作为参数来指代当前实体（mcid == X，X大于0，不影响）
                    if (QuestModule.Instance.IsAcceptable(questCid, null))
                    {
                        data.isOfferingQuest = true;
                        break;
                    }
                }
            }

            //通过存档覆盖数据
            if (savePOD != null)
            {
                data.position = GameMathUtil.LogicPos2UnityPos(savePOD.x, savePOD.y, savePOD.z);
                data.rotation = GameMathUtil.IntEuler2Quaternion(savePOD.dirEuler);
                data.isObstacleActive = savePOD.normal.isObstacleActive;
                data.isCheckPointActive = savePOD.normal.isCheckPointActive;
            }

            mapData.entities.Add(data);

            //如果有configLib，添加完一个需要从configLib里移除
            configLib?.Remove(config.id);
        }

        /// <summary>
        /// 获取实体位置，注意只能获取当前地图周围实体的位置，如果因为aoi实体没有同步给客户端则无法获取位置
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public static Vector3 GetEntityPos(int id)
        {
            switch (GameDataCenter.CurrGameArea)
            {
                case GameplayAreaType.MainCity:
                    var maincityEntityCtrl = MainCityModule.Instance.mainController.regionCtrl.entityCtrl;
                    var maincityEntity = maincityEntityCtrl.GetEntityById(id);
                    if (maincityEntity == null)
                    {
                        LogGame.LogError("找不到实体，请确认实体就在周围，该接口无法获取其他地图或不在周围的实体");
                        return default;
                    }

                    return maincityEntity.transform.position;
                case GameplayAreaType.Level:
                    var levelEntityCtrl = LevelPlayModule.Instance.levelPlay.regionCtrl.entityCtrl;
                    if (!levelEntityCtrl.entitys.TryGetValue(id, out var levelEntity))
                    {
                        LogGame.LogError("找不到实体，请确认实体就在周围，该接口无法获取其他地图或不在周围的实体");
                        return default;
                    }

                    return levelEntity.transform.position;
                default:
                    return default;
            }
        }
    }
}